package com.spzx.common.rabbit.config;

import com.alibaba.fastjson2.JSON;
import com.spzx.common.rabbit.entity.GuiguCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RabbitInitConfigApplicationListener implements ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        //开启消息确认模式和回退模式：注册两个回调函数
        this.setCallback();
    }

    private void setCallback() {
        //注册确认回调函数： 生产者发消息到交换机，不管交换机是否收到消息，都会回调confirm函数
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if(ack){ //ack=true 表示交换机收到消息了
                log.info("交换机收到消息了:{}",correlationData); //CorrelationData发送消息时，可以携带关联数据。
            }else{ //ack=false 表示交换机没收到消息（没有匹配到交换机）
                log.error("交换机没收到消息:{}",cause); // cause 异常信息。一般交换机名称指定错了，找不到交换机。

                //重发消息
                retrySendMsg(correlationData);
            }
        });

        //注册回退函数：当交换机发消息给队列失败才会执行回调returnedMessage函数。如果交换机发消息到队列成功。则不执行回调。
        rabbitTemplate.setReturnsCallback(returned -> {

            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            log.error("Returned: " + returned.getMessage() + "\nreplyCode: " + returned.getReplyCode()
                    + "\nreplyText: " + returned.getReplyText() + "\nexchange/rk: "
                    + returned.getExchange() + "/" + returned.getRoutingKey()); // returned.getRoutingKey()发送指定路由key

            //重发消息
            String uuid2 = returned.getMessage().getMessageProperties().getCorrelationId();
            String uuid = returned.getMessage().getMessageProperties().getHeader("spring_returned_message_correlation");
            log.info("uuid2="+uuid2);
            log.info("uuid="+uuid);
            String correlationDataStr = (String)redisTemplate.opsForValue().get(uuid);
            GuiguCorrelationData guiguCorrelationData = JSON.parseObject(correlationDataStr, GuiguCorrelationData.class);

            //todo 方式一:如果不考虑延迟消息重发 直接返回
            if(guiguCorrelationData.isDelay()){
                return;
            }

            retrySendMsg(guiguCorrelationData);
        });

    }

    //重新发送消息
    private void retrySendMsg(CorrelationData correlationData) {
        GuiguCorrelationData guiguCorrelationData = (GuiguCorrelationData)correlationData;
        int retryCount = guiguCorrelationData.getRetryCount();
        if(retryCount>=3){
            log.info("重试次数达到了最大次数；不再重试了.人工处理"); //保存消息到数据库。发短信，发邮件。
            return;
        }

        retryCount++;
        guiguCorrelationData.setRetryCount(retryCount);
        redisTemplate.opsForValue().set(guiguCorrelationData.getId(), JSON.toJSONString(guiguCorrelationData),10, TimeUnit.MINUTES);


        //重发
//        rabbitTemplate.convertAndSend(guiguCorrelationData.getExchange(),
//                guiguCorrelationData.getRoutingKey(),
//                guiguCorrelationData.getMessage(),
//                guiguCorrelationData);


        if(guiguCorrelationData.isDelay()){ //发延迟消息 重发
            rabbitTemplate.convertAndSend(guiguCorrelationData.getExchange(),
                    guiguCorrelationData.getRoutingKey(),
                    guiguCorrelationData.getMessage(),
                    (message1)->{
                        message1.getMessageProperties().setDelay(guiguCorrelationData.getDelayTime()*1000);
                        return message1;
                    },
                    guiguCorrelationData);
        }else{ //普通消息重发
                rabbitTemplate.convertAndSend(guiguCorrelationData.getExchange(),
                guiguCorrelationData.getRoutingKey(),
                guiguCorrelationData.getMessage(),
                guiguCorrelationData);
        }
    }

}
